package src;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;

import org.jdom2.*;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.*;

import Exception.FondCellException;
import Exception.IndividuInCellException;
import Exception.ObjetDynamiqueCellException;


public class LecteurXML {
	
	private static Hashtable<Integer,ArrayList<Individu>> combats = new Hashtable<Integer,ArrayList<Individu>>() ; 
	private static Hashtable<String, ArrayList<Integer>> tribuQg = new Hashtable<String, ArrayList<Integer>>();
	private static ArrayList<Individu> individus = new ArrayList<Individu>();
	
	//methodes
	
	public LecteurXML(){
	}
	
	private static void addAttributeTo(Element parent, String attributeName, int i){
		Attribute att = new Attribute(attributeName,Integer.toString(i));
		parent.setAttribute(att);
	}
	
	private static void addChildTo(Element parent, String nodeName, int i){
		Element childNode = new Element(nodeName);
		childNode.setText(Integer.toString(i));
		parent.addContent(childNode);
	}
	
	private static void addChildTo(Element parent, String nodeName, String s){
		Element childNode = new Element(nodeName);
		childNode.setText(s);
		parent.addContent(childNode);
	}
	
	private static void addChildTo(Element parent, String nodeName, boolean b){
		Element childNode = new Element(nodeName);
		childNode.setText(Boolean.toString(b));
		parent.addContent(childNode);
	}
	
	private static void addChildTo(Element parent, String nodeName, TypeFond t){
		Element childNode = new Element(nodeName);
		childNode.setText(t.name());
		parent.addContent(childNode);
	}
	
	private static void addChildTo(Element parent, String nodeName, Direction d){
		Element childNode = new Element(nodeName);
		childNode.setText(d.name());
		parent.addContent(childNode);
	}
	
	//a tester
	public static Element ecrireIndividu(Individu i){ 
		Element racine = new Element("Individu");
		
		addChildTo(racine, "ClassName", i.getClass().getName());
		addChildTo(racine, "PositionX", i.getX());
		addChildTo(racine, "PositionY", i.getY());
		addChildTo(racine, "QGX", i.getxQG());
		addChildTo(racine, "QGY", i.getyQG());
		addChildTo(racine, "Collision", i.isCollision());
		addChildTo(racine, "Sprite", i.getSprite());
		addChildTo(racine, "PV", i.getPv());
		addChildTo(racine, "Satiete", i.getSatiete());
		addChildTo(racine, "Herbivore", i.isHerbivore());
		addChildTo(racine, "Carnivore", i.isCarnivore());
		addChildTo(racine, "Puissance", i.getPuissance());
		addChildTo(racine, "Resistance", i.getResistance());
		addChildTo(racine, "Tribu", i.getTribu());
		addChildTo(racine, "Vitesse", i.getVitesse());
		addChildTo(racine, "Direction", i.getDirection());
		addChildTo(racine, "IdCombat", i.getIdCombat());
		addChildTo(racine, "CompteurTemps", i.getCompteurTemps());
		if(i.getObjetTenu() != null){
			racine.addContent(ecrireObjetMap(i.getObjetTenu(),"ObjetTenu"));
		}
		if(i.getEquipDef() != null){
			racine.addContent(ecrireObjetMap(i.getEquipDef(),"EquipDef"));
		}
		if(i.getEquipOff() != null){
			racine.addContent(ecrireObjetMap(i.getEquipOff(),"EquipOff"));
		}
		
		return racine;
	}
	
	public static Element ecrireFond(Fond fond){ 
		Element racine = new Element("Fond");
		addChildTo(racine, "Type", fond.getType());
		addChildTo(racine, "Collision", fond.isCollision());
		addChildTo(racine, "Sprite", fond.getSprite());
		return racine;
	}
	
	public static Element ecrirePheromone(Hashtable<String,Pheromone> pheromone){ 
		Element racine = new Element("Pheromones");
		Enumeration<String> keys = pheromone.keys();
		while(keys.hasMoreElements()){
			String currentKey = keys.nextElement();
			Pheromone p = pheromone.get(currentKey);
			Element phero = new Element("Pheromone");
			
			addChildTo(phero, "From", currentKey);
			addChildTo(phero, "PositionX", p.getX());
			addChildTo(phero, "PositionY", p.getY());
			addChildTo(phero, "Intensite", p.getIntensite());
			addChildTo(phero, "Collision", p.isCollision());
			
			racine.addContent(phero);
		}
		
		
		return racine;
	}
	
	private static Element ecrireObjetMap(ObjetMap p, String type){ 
		Element racine = new Element(type);
		
		addChildTo(racine, "ClassName", p.getClass().getName());
		addChildTo(racine, "PositionX", p.getX());
		addChildTo(racine, "PositionY", p.getY());
		addChildTo(racine, "Quantite", p.getQuantite());
		addChildTo(racine, "Collision", p.isCollision());
		addChildTo(racine, "Sprite", p.getSprite());
		
		return racine;
		
		
	}
	
	public static Element ecrireObjetMapForCell(Hashtable<String,ObjetMap> hashObj){ 
		Element racine = new Element("ObjetMapList");	
		Enumeration<String> keys = hashObj.keys();
		while(keys.hasMoreElements()){
			String currentKey = keys.nextElement();
			ObjetMap p = hashObj.get(currentKey);
			Element obj = new Element("ObjetMap");
			
			addChildTo(racine, "Key", currentKey);
			addChildTo(racine, "ClassName", p.getClass().getName());
			addChildTo(racine, "PositionX", p.getX());
			addChildTo(racine, "PositionY", p.getY());
			addChildTo(racine, "Quantite", p.getQuantite());
			addChildTo(racine, "Collision", p.isCollision());
			addChildTo(racine, "Sprite", p.getSprite());
			
			racine.addContent(obj);
		}
		
		return racine;
	}
	
	public static Element ecrireObjetMap(ObjetMap p){ 
		Element racine = new Element("ObjetMap");
		
		addChildTo(racine, "ClassName", p.getClass().getName());
		addChildTo(racine, "PositionX", p.getX());
		addChildTo(racine, "PositionY", p.getY());
		addChildTo(racine, "Quantite", p.getQuantite());
		addChildTo(racine, "Collision", p.isCollision());
		addChildTo(racine, "Sprite", p.getSprite());
		
		return racine;
	}
	
	public static Element ecrireCellForGame(Cell c){ 
		Element racine = new Element("Cell");
		
		addAttributeTo(racine, "PosX", c.getX());
		addAttributeTo(racine, "PosY", c.getY());
		addChildTo(racine, "QG", Boolean.toString(c.isQg()));
		
		if (c.getPheromones()!=null){
			Element phero = ecrirePheromone(c.getPheromones());
			racine.addContent(phero);
		}
		
		if (c.getIndividu()!=null){
			Element individu = ecrireIndividu(c.getIndividu());
			racine.addContent(individu);
		}
		
		if (c.getFond()!=null){
			Element fond = ecrireFond(c.getFond());
			racine.addContent(fond);
		}
		
		if (c.getObjet()!=null){
			Element objet = ecrireObjetMapForCell(c.getObjet());
			racine.addContent(objet);
		}
		
		return racine;
	}
	
	public static Element ecrireCellForMap(Cell c){ 
		Element racine = new Element("Cell");
		
		addAttributeTo(racine, "PosX", c.getX());
		addAttributeTo(racine, "PosY", c.getY());
		addChildTo(racine, "QG", Boolean.toString(c.isQg()));
		
		if (c.getFond()!=null){
			Element fond = ecrireFond(c.getFond());
			racine.addContent(fond);
		}

		return racine;
	}
	
	public static Element ecrireMap(Map map){ 
		Element racine = new Element("Map");
		
		addChildTo(racine, "Size", map.getSize());
		addChildTo(racine, "Name", map.getName());
		
		Element cells = new Element("Cells");
		for (ArrayList<Cell> arrayCase : map.getMap()) {
			for (Cell case1 : arrayCase) {
				cells.addContent(ecrireCellForMap(case1));
			}
		}
		
		racine.addContent(cells);
		return racine;
	}
	
	public static Element ecrireGame(Map map){ 
		Element racine = new Element("Map");
		
		addChildTo(racine, "Size", map.getSize());
		addChildTo(racine, "Name", map.getName());
		
		Element cells = new Element("Cells");
		for (ArrayList<Cell> arrayCase : map.getMap()) {
			for (Cell case1 : arrayCase) {
				cells.addContent(ecrireCellForGame(case1));
			}
		}
		
		racine.addContent(cells);
		return racine;
	}
	
	
	public static void enregistre(org.jdom2.Document document, String fichier) throws FileNotFoundException, IOException
	{
      XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
      sortie.output(document, new FileOutputStream(fichier));
	}
	
	private static int getSize(Element root){
		String size = root.getChild("Size").getText();
		int s = Integer.parseInt(size);
		return s;
	}
	
	private static String getName(Element root){
		String name = root.getChild("Name").getText();
		return name;
	}
	
	private static List getCells(Element root){
		Element cells = root.getChild("Cells");
		List listCells =  (List) cells.getChildren("Cell");
		return listCells;
	}
	
	private static void applyFondToMap(Map map,int x, int y, Element elem){
		String type = elem.getChild("Type").getText();	
		map.addBackgroundToCell(TypeFond.valueOf(type), map.getCell(x, y));
	}
	
	
	private static void applyIndividuToGame(Map game,int x, int y, Element elem) 
			throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException, IndividuInCellException, 
			ObjetDynamiqueCellException, FondCellException{
		
		String className = elem.getChild("ClassName").getText();
		int posX = Integer.parseInt(elem.getChild("PositionX").getText());
		int posY = Integer.parseInt(elem.getChild("PositionY").getText());
		int qgX = Integer.parseInt(elem.getChild("QGX").getText());
		int qgY = Integer.parseInt(elem.getChild("QGY").getText());
		int pv = Integer.parseInt(elem.getChild("PV").getText());
		int satiete = Integer.parseInt(elem.getChild("Satiete").getText());
		boolean herbivore = Boolean.parseBoolean(elem.getChild("Herbivore").getText());
		boolean carnivore = Boolean.parseBoolean(elem.getChild("Carnivore").getText());
		int puissance = Integer.parseInt(elem.getChild("Puissance").getText());
		int resistance = Integer.parseInt(elem.getChild("Resistance").getText());
		String tribu = elem.getChild("Tribu").getText();
		int vitesse = Integer.parseInt(elem.getChild("Vitesse").getText());
		Direction d = Direction.valueOf(elem.getChild("Direction").getText());
		Arme arme;
		Armure armure;
		ObjetMap obj;
		if(elem.getChild("EquipOff")!=null){
			arme = (Arme) getObjectFromXml(elem.getChild("EquipOff"));
		}
		else{
			 arme = null;
		}
		if(elem.getChild("EquipDef")!=null){
			armure = (Armure) getObjectFromXml(elem.getChild("EquipDef"));
		}
		else{
			armure = null;
		}
		if(elem.getChild("ObjetTenu")!=null){
			obj = getObjectFromXml(elem.getChild("ObjetTenu"));
		}
		else{
			 obj = null;
		}
		
		int idCombat = Integer.parseInt(elem.getChild("IdCombat").getText());;	
		int cmpt = Integer.parseInt(elem.getChild("CompteurTemps").getText());;
		
		if(!tribuQg.containsKey(tribu)){
			tribuQg.put(tribu, new ArrayList<Integer>());
			tribuQg.get(tribu).add(qgX);
			tribuQg.get(tribu).add(qgY);
		}
		
	    Class<Individu> cl = (Class<Individu>)Class.forName(className);                            
		Class[] types = new Class[]{int.class, int.class,int.class, boolean.class, boolean.class, String.class, 
									int.class, int.class,int.class, Direction.class, Arme.class, Armure.class,ObjetDynamique.class,
									int.class,int.class,int.class,int.class,int.class};
	    Constructor<Individu> ct = (Constructor<Individu>)cl.getConstructor(types);     
	    Individu i = ct.newInstance(posX,posY,pv,herbivore,carnivore,tribu,puissance,resistance,vitesse,d,arme,armure,obj,idCombat,cmpt,satiete,qgX,qgY);
	    
	    if(i.getIdCombat() != -1){
	    	int id = i.getIdCombat();
	    	if(combats.containsKey(id)){
	    		combats.get(id).add(i);
	    	}
	    	else{
	    		combats.put(id,  new ArrayList<Individu>());
	    		combats.get(id).add(i);	    		
	    	}
	    }
	    individus.add(i);
	    game.addIndividuToCell(i, game.getCell(x, y));
		
	}
	
	private static void applyPheromonesToGame(Map game,int x, int y, Element elem){
		Hashtable<String, Pheromone> hashList = new Hashtable<String, Pheromone>();
		List listPhero = elem.getChildren("Pheromone");	
		Iterator<Element> i = listPhero.iterator();
	    while(i.hasNext())
	    {    
	       Element courant = (Element)i.next();
	       String from = courant.getChild("From").getText();
	       int posX = Integer.parseInt(courant.getChild("PositionX").getText());
	       int posY = Integer.parseInt(courant.getChild("PositionX").getText());
	       int intens = Integer.parseInt(courant.getChild("Intensite").getText());
	       Pheromone phero = new Pheromone( posX,  posY, intens);
	       hashList.put(from,phero);
	    }
		game.getCell(x, y).setPheromones(hashList);
		
	}
	
	private static ObjetMap getObjectFromXml(Element elem) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException{
		
		String className = elem.getChild("ClassName").getText();
		int posX = Integer.parseInt(elem.getChild("PositionX").getText());
		int posY = Integer.parseInt(elem.getChild("PositionY").getText());
		int qte = Integer.parseInt(elem.getChild("Quantite").getText());
		
		Class<ObjetMap> cl = (Class<ObjetMap>)Class.forName(className);                            
		Class[] types = new Class[]{int.class, int.class, int.class};
	    Constructor<ObjetMap> ct = (Constructor<ObjetMap>)cl.getConstructor(types);     
	    ObjetMap o = ct.newInstance(posX,posY,qte);
	    
	    return o;
	}
	
	private static void applyHashtableObjectToGame(Map game,int x, int y, Element elem) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException{
		Hashtable<String,ObjetMap> hashObj = new Hashtable<String,ObjetMap>();
		List listObj =  (List) elem.getChildren("ObjetMap");
		Iterator<Element> i = listObj.iterator();
	    while(i.hasNext())
	    {    
	       Element courant = (Element)i.next();
	       ObjetMap obj = getObjectFromXml(courant);
	       String key = courant.getChild("Key").getText();
	       hashObj.put(key, obj);
	    }
		game.getCell(x, y).setObjet(hashObj);
	}

//	private static void applyObjectToGame(Map game,int x, int y, Element elem) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException{
//		ObjetDynamique obj = getObjectFromXml(elem);
//		game.getCell(x, y).setObjet(obj);
//	}
	
	private static void applyCellElementToMap(Map map, Element elem){
		int posX = Integer.parseInt(elem.getAttributeValue("PosX"));
		int posY = Integer.parseInt(elem.getAttributeValue("PosY"));
		boolean qg = Boolean.parseBoolean(elem.getAttributeValue("QG"));
		
		map.getCell(posX, posY).setX(posX);
		map.getCell(posX, posY).setY(posY);
		map.getCell(posX, posY).setQg(qg);
		
		if(elem.getChild("Fond") != null){
			applyFondToMap( map, posX, posY, elem.getChild("Fond"));
		}		
	}
	
	private static void applyCellElementToGame(Map game, Element elem) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IndividuInCellException, ObjetDynamiqueCellException, FondCellException{
		int posX = Integer.parseInt(elem.getAttributeValue("PosX"));
		int posY = Integer.parseInt(elem.getAttributeValue("PosY"));
		boolean qg = Boolean.parseBoolean(elem.getAttributeValue("QG"));
		
		game.getCell(posX, posY).setX(posX);
		game.getCell(posX, posY).setY(posY);
		game.getCell(posX, posY).setQg(qg);
		
		if(elem.getChild("Fond") != null){
			applyFondToMap( game, posX, posY, elem.getChild("Fond"));
			
			if(elem.getChild("Individu") != null){
				applyIndividuToGame( game, posX, posY, elem.getChild("Individu"));
				//afficheElement(elem.getChild("Individu"));
			}
			if(elem.getChild("Pheromones") != null){
				applyPheromonesToGame( game, posX, posY, elem.getChild("Pheromones"));
			}
			if(elem.getChild("ObjetMap") != null){
				applyHashtableObjectToGame( game, posX, posY, elem.getChild("ObjetMapList"));
			}
		}		
	}
	
	public static Map loadMap(String mapName) throws JDOMException, IOException{
		Document document = loadXMLMap(mapName);
		Element root = document.getRootElement();
		
		int size = getSize(root);
		Map map = new Map(size);
		map.setName(getName(root));
		
		List listCells = getCells(root);
		
		Iterator<Element> i = listCells.iterator();
	    while(i.hasNext())
	    {    
	       Element courant = (Element)i.next();
	       applyCellElementToMap(map, courant);
	    }
		
		return map;
	}
	
	public static Map loadGame(String gameName) throws JDOMException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IndividuInCellException, ObjetDynamiqueCellException, FondCellException{
		combats.clear();
		Document document = loadXMLGame(gameName);
		Element root = document.getRootElement();
		
		int size = getSize(root);
		Map game = new Map(size);
		game.setName(getName(root));
		
		List listCells = getCells(root);
		
		Iterator<Element> i = listCells.iterator();
	    while(i.hasNext())
	    {    
	       Element courant = (Element)i.next();
	       applyCellElementToGame(game, courant);
	    }
	    Individu.setCombat(combats);
	    Individu.setTribuQg(tribuQg);
	    Map.setIndividus(individus);
		return game;
	}
	
	private static Document loadXMLMap(String mapName) throws JDOMException, IOException{
		SAXBuilder sxb = new SAXBuilder();
		return sxb.build(new File(mapName));
	}
	
	private static Document loadXMLGame(String gameName) throws JDOMException, IOException{
		SAXBuilder sxb = new SAXBuilder();
		return sxb.build(new File(gameName));
	}
	
	public static void afficheElement(Element e)
	{
	   try
	   {
	      XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
	      sortie.output(e, System.out);
	   }
	   catch (java.io.IOException e1){
		   
	   }
	}
	

}
